/*
 * File : spi_FM25V05.c
 *
 */
#include "config.h"
#include "memory.h"
#include "xstring.h"
#include "spi_FM25V05.h"
#include "task.h"
#include "type.h"
#include "stm32f10x_spi.h"


/**
 * @def  	SPI_SRAM_CS_LOW
 * @brief	Select SPI SRAM: ChipSelect pin low
 */
#define SPI_SRAM_CS_LOW()	GPIO_ResetBits(GPIOB, GPIO_Pin_12)
/**
 * @def  	SPI_RSAM_CS_HIGH
 * @brief	Deselect SPI SRAM: ChipSelect pin high 
 */
#define SPI_SRAM_CS_HIGH()	GPIO_SetBits(GPIOB, GPIO_Pin_12)

/* SRAM command define */
#define WREN		0x06	///< Write enable
#define WRDI		0x04	///< Write Disable
#define RDSR		0x05	///< Read Status Register
#define WRSR		0x01	///< Write Status Register
#define READ		0x03	///< Read Memory Data
#define FSTRD		0x0B	///< Fast Read Memory Data
#define WRITE		0x02	///< Write Memory Data
#define SLEEP		0xB9	///< Enter Sleep Mode
#define RDID		0x9F	///< Read identification
#define SNR			0xC3	///< Read S/N

#define WIP_Flag	0x01	///< Write In Progress (WIP) flag
#define Dummy_Byte	0xA5	///< Dummy byte

/** @name SPI STATUS CODE DEFINE
 * @{
 */
#define SPI_SRAM_READY		0	///< Can read, write right now
#define SPI_SRAM_READ		1	///< SRAM reading
#define SPI_SRAM_WRITE		2	///< SRAM writing
/** @} */
static unsigned char SPI_SRAM_Status;	///< SRAM status

static unsigned char *gBuf;		///< read/write buffer pointer
static unsigned short gCount;	///< read/write buffer count
static var32 gAddr;				///< read/write SRAM address

static unsigned char gWState = 0;	///< write state
static unsigned short gWCount;	///< write count
static unsigned char gWMode;	///< write mode

/* write state define */
#define SRAM_STATE_IDLE		0	///< idle
#define SRAM_STATE_WRITE	1	///< start write
#define SRAM_STATE_CHECK	2	///< check write finish

unsigned short SPI_SRAM_TimeOut;

/* Erase Cycle Time Out define */
#define TIMEOUT_SRAM	500

/**
 * @brief	Wait SPI SRAM Ready to do next action
 * @param	none
 * @return	SPI_SRAM_OK  : ready
 *			SPI_SRAM_ERR : timeout
 */
unsigned char SPI_SRAM_Wait_Ready(void)
{
	unsigned long temp = gSystick;
	unsigned long timeout;
	
	while (SPI_SRAM_Status != SPI_SRAM_READY)
	{
		timeout =  (gSystick - temp);
		if (timeout > TIMEOUT_SRAM)
		{
			*pFlag_err_sram_timeout = 1;
			return SPI_SRAM_ERR;
		}
	}
	return SPI_SRAM_OK;
}


/**
 * @brief	Send a byte to SRAM
 * @param	byte	data
 * @return	read data from SPI after send one byte
 * @note
 */
static unsigned char SPI_SRAM_SendByte(unsigned char byte)
{
	unsigned long temp;
	unsigned long timeout;
	
	temp = gSystick;
	/* Loop while DR register in not emplty */
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
	{
		timeout = gSystick - temp;
		if (timeout > 10)
		{
			*pFlag_err_sram = 1;
			return SPI_SRAM_ERR;
		}
	}
	/* Send byte through the SPI2 peripheral */
	SPI_I2S_SendData(SPI2, byte);
	/* Wait to receive a byte */
	temp = gSystick;
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
	{
		timeout = gSystick - temp;
		if (timeout > 10)
		{
			*pFlag_err_sram = 1;
			return SPI_SRAM_ERR;
		}
	}
	/* Return the byte read from the SPI bus */
	return SPI_I2S_ReceiveData(SPI2);
}

/**
 * @brief	Send Write Enable command to SRAM
 * @param	none
 * @return	none
 * @note
 */
static void SPI_SRAM_WriteEnable(void)
{
	SPI_SRAM_CS_LOW();
	SPI_SRAM_SendByte(WREN);
	SPI_SRAM_CS_HIGH();
}

/**
 * @brief	Send Wake UP command to SRAM
 * @param	none
 * @return	none
 * @note
 */
static void SPI_SRAM_WakeUP(void)
{
	SPI_SRAM_CS_LOW();
	SPI_SRAM_SendByte(Dummy_Byte);
	SPI_SRAM_CS_HIGH();
}

/**
 * @brief	Send Deep sleep command to SRAM
 * @param	none
 * @return	none
 * @note
 */
static void SPI_SRAM_Sleep(void)
{
	SPI_SRAM_CS_LOW();
	SPI_SRAM_SendByte(SLEEP);
	SPI_SRAM_CS_HIGH();
}

/**
 * @brief	SRAM write task
 * @param	none
 * @return	none
 * @note
 */
void SPI_SRAM_Hook(void)
{
	unsigned short page_remain;
	unsigned char status;
	
	switch (gWState)
	{
	default:
	case SRAM_STATE_IDLE:
		break;
	case SRAM_STATE_WRITE:
		*pFlag_pwr_sram = 1;
		/* Start write a page */
		*pFlag_spi2_busy	= 1;
		page_remain		= 256 - gAddr.u8.b0;
		if (gWCount > page_remain)
		{
			gCount	= page_remain;
			gWCount	-= page_remain;
		} else {
			gCount	= gWCount;
			gWCount	= 0;
		}
		SPI_SRAM_CS_HIGH();
		SPI_SRAM_WriteEnable();
		SPI_SRAM_CS_LOW();
		SPI_SRAM_SendByte(gWMode);
		SPI_SRAM_SendByte(gAddr.u8.b2);
		SPI_SRAM_SendByte(gAddr.u8.b1);
		SPI_SRAM_SendByte(gAddr.u8.b0);
		SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_TXE | SPI_I2S_IT_RXNE, ENABLE);
		gAddr.u32 += page_remain;
		gWState = SRAM_STATE_CHECK;
		break;
	case SRAM_STATE_CHECK:
		/* check write finish */
		if (*pFlag_spi2_busy == 0)
		{
			/* send finish */
			SPI_SRAM_CS_LOW();
			SPI_SRAM_SendByte(RDSR);
			status = SPI_SRAM_SendByte(Dummy_Byte);
			SPI_SRAM_CS_HIGH();
			if ((status & WIP_Flag) == 0)
			{
				/* write finish */
				if (gWCount == 0)
				{
					/* write finish */
					gWState = SRAM_STATE_IDLE;
					*pFlag_pwr_sram = 0;
					SPI_SRAM_Status = SPI_SRAM_READY;
				}
				else
				{
					/* next page */
					gWState = SRAM_STATE_WRITE;
				}
			}
		}
		break;
	}
}

/**
 * @brief	Write data to SRAM
 * @param	addr	SRAM address
 * @param	buf		buffer address
 * @param	count	length (base:byte)
 * @return	SPI_SRAM_OK/SPI_SRAM_ERR, See SPI ERROR CODE DEFINE
 */
static void SPI_SRAM_Write_Init(	unsigned long addr,
									unsigned char *buf,
									unsigned short count)
{
	int i;

	for (; count != 0;)
	{
		if (SPI_SRAM_Wait_Ready() == SPI_SRAM_OK)
		{
			SPI_SRAM_Status = SPI_SRAM_WRITE;
			if (count > 256)
				i = 256;
			else
				i = count;
			count -= i;
			xmemcpy((void *)pSRAMData, (void *)buf, i);
			gAddr.u32	= addr;
			gBuf		= pSRAMData;
			gWCount		= i;
			gWState		= SRAM_STATE_WRITE;
			addr		+= i;
			buf			+= i;
		}
	}
	
}

/** @ingroup SPI_SRAM Cargo SPI_SRAM
 * @{
 */
/**
 * @brief	Read SRAM ID
 * @param	none
 * @return	SRAM ID
 * @note	Return 0 means SRAM busy.
 */
unsigned long SPI_SRAM_ReadID(void)
{
	var32 id;

	id.u32 = 0;
	
	if (SPI_SRAM_Status == SPI_SRAM_READY)
	{
		*pFlag_pwr_sram = 1;
		SPI_SRAM_CS_LOW();
		SPI_SRAM_SendByte(RDID);
		id.u8.b2 = SPI_SRAM_SendByte(Dummy_Byte);
		id.u8.b1 = SPI_SRAM_SendByte(Dummy_Byte);
		id.u8.b0 = SPI_SRAM_SendByte(Dummy_Byte);
		SPI_SRAM_CS_HIGH();
		*pFlag_pwr_sram = 0;
		SPI_SRAM_Status = SPI_SRAM_READY;
	}
	return id.u32;

}

/**
 * @brief	Read data from SRAM
 * @param	addr	SRAM address
 * @param	buf		buffer address
 * @param	count	length (base:byte)
 * @return	SPI_SRAM_OK/SPI_SRAM_ERR, See SPI ERROR CODE DEFINE
 */
unsigned char SPI_SRAM_Read(	unsigned long addr,
								unsigned char *buf,
								unsigned short count)
{
	var32 address;

	if (SPI_SRAM_Wait_Ready() == SPI_SRAM_OK)
	{
		*pFlag_pwr_sram = 1;
		address.u32 = addr;
		gBuf		= buf;
		gCount		= count;
		/* change status to READ */	
		SPI_SRAM_Status = SPI_SRAM_READ;
		
		SPI_SRAM_CS_HIGH();
		SPI_SRAM_CS_LOW();
		SPI_SRAM_SendByte(READ);
		SPI_SRAM_SendByte(address.u8.b2);
		SPI_SRAM_SendByte(address.u8.b1);
		SPI_SRAM_SendByte(address.u8.b0);
		SPI_I2S_SendData(SPI2, Dummy_Byte);
		/* Enable SPI RXNE interrupt */
		SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE);
		return SPI_SRAM_OK;
	}
	return SPI_SRAM_ERR;
}

/**
 * @brief	Write data to SRAM
 * @param	addr	SRAM address
 * @param	buf		buffer address
 * @param	count	length (base:byte)
 * @return	SPI_SRAM_OK/SPI_SRAM_ERR, See SPI ERROR CODE DEFINE
 */
unsigned char SPI_SRAM_Write(	unsigned long addr,
								unsigned char *buf,
								unsigned short count)
{
	if (SPI_SRAM_Wait_Ready() == SPI_SRAM_OK)
	{
		gWMode = WRITE;
		SPI_SRAM_Write_Init(addr, buf, count);
		return SPI_SRAM_OK;
	}
	return SPI_SRAM_ERR;
}
/**
 * @brief	Write data to SRAM
 * @param	addr	SRAM address
 * @param	buf		buffer address
 * @param	count	length (base:byte)
 * @return	SPI_SRAM_OK/SPI_SRAM_ERR, See SPI ERROR CODE DEFINE
 */
unsigned char SPI_SRAM_Fast_Write(	unsigned long addr,
									unsigned char *buf,
									unsigned short count)
{
	if (SPI_SRAM_Wait_Ready() == SPI_SRAM_OK)
	{
		gWMode = WRITE;
		SPI_SRAM_Write_Init(addr, buf, count);
		return SPI_SRAM_OK;
	}
	return SPI_SRAM_ERR;
}


/**
 * @brief	SPI SRAM initial
 * @param	id	SRAM ID
 * @return	SPI_SRAM_OK/SPI_SRAM_ERR, See SPI ERROR CODE DEFINE
 * @note
 */
unsigned char SPI_SRAM_Init(unsigned long id)
{
	unsigned char retry = 3;
	GPIO_InitTypeDef	GPIO_InitStructure;
	NVIC_InitTypeDef	NVIC_InitStructure;
	SPI_InitTypeDef		SPI_InitStructure;

	/* Clock Setting */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

	/* Enable the RTC Interrupt */
	NVIC_InitStructure.NVIC_IRQChannel				= SPI2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority	= IRQ_SPI2;
	NVIC_InitStructure.NVIC_IRQChannelCmd			= ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	/* I/O Setting */
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed	= GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin		= 0x00
									| GPIO_Pin_13		//PA5 SCK
									| GPIO_Pin_14		//PA6 MISO
									| GPIO_Pin_15		//PA7 MOSI
									;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin		= 0x00
									| GPIO_Pin_12		//PA4 NSS
									;
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_Out_PP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	SPI_SRAM_CS_HIGH();
	/* Clock Setting */
	//RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
	/* SPI2 configuration */
	SPI_InitStructure.SPI_Direction			= SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode				= SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize			= SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL				= SPI_CPOL_High;
	SPI_InitStructure.SPI_CPHA				= SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_NSS				= SPI_NSS_Soft;
	if (SYS_CLK == SYS_CLK_48M)
		SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
	else
		SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
	SPI_InitStructure.SPI_FirstBit			= SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial		= 7;
	SPI_Init(SPI2, &SPI_InitStructure);
	/* Interrupt setting */
	SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_TXE | SPI_I2S_IT_RXNE, DISABLE);
	/*  Enable SPI2 */
	SPI_Cmd(SPI2, ENABLE);

	SPI_SRAM_Status = SPI_SRAM_READY;
	
	while (retry--)
	{
		if (id == SPI_SRAM_ReadID())
		{
			return SPI_SRAM_OK;
		}
	}
	

	return SPI_SRAM_ERR;
}

void SPI_SRAM_Power_On(void)
{
	SPI_SRAM_WakeUP();
}
void SPI_SRAM_Power_Off(void)
{
	SPI_SRAM_Wait_Ready();
	SPI_SRAM_Sleep();
}

/**
 * @brief	SPI2 IRQ callback function
 * @param	none
 * @return	none
 * @note
 */
void SPI2_IRQ_CALLBACK(void)
{
	if (SPI_SRAM_Status == SPI_SRAM_READ)
	{
		/* Read */
		if(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) != RESET)
		{
			*gBuf++ = SPI_I2S_ReceiveData(SPI2);
			if (--gCount == 0)
			{
				SPI_SRAM_CS_HIGH();
				SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, DISABLE);
				*pFlag_pwr_sram = 0;
				SPI_SRAM_Status = SPI_SRAM_READY;
			}
			else
			{
				SPI_I2S_SendData(SPI2, Dummy_Byte);
			}
		}
	}
	if (SPI_SRAM_Status == SPI_SRAM_WRITE)
	{
		/* Write */
		if(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) != RESET)
		{
			if (gCount-- == 0)
			{
				SPI_SRAM_CS_HIGH();
				SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_TXE, DISABLE);
				*pFlag_spi2_busy	= 0;
			}
			else
			{
				SPI_I2S_SendData(SPI2, *gBuf++);
			}
		}
		if(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) != RESET)
		{
			SPI_I2S_ReceiveData(SPI2);
			if (gCount == 0)
			{
				SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, DISABLE);
			}
			
		}
	}
}

void SPI_SRAM_DisadbleProtection(void)
{
        SPI_SRAM_CS_HIGH();
        /* Enable the write access to the SRAM */
        SPI_SRAM_WriteEnable();
        /* Select the SRAM: Chip Select low */
        SPI_SRAM_CS_LOW();
        SPI_SRAM_SendByte(WRSR);
        /* Send WriteAddr high nibble address byte to write to */
        SPI_SRAM_SendByte(0x00);
		
        SPI_SRAM_CS_HIGH();
}

/*******************************************************************************
* Function Name  : SPI_SRAM_WaitForWriteEnd
* Description    : Polls the status of the Write In Progress (WIP) flag in the  
*                  SRAM's status  register  and  loop  until write  opertaion
*                  has completed.  
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
u8 SRAMBusy;
void SPI_SRAM_WaitForWriteEnd(void)
{
	u8 Status;
	static short TimeOutTimer = 1000;	//1ms
	SRAMBusy = 0x00;
	/* Loop as long as the memory is busy with a write cycle */
	do
	{
		Status = 0;
		/* Select the SRAM: Chip Select low */
		SPI_SRAM_CS_LOW();
		
		/* Send "Read Status Register" instruction */
		SPI_SRAM_SendByte(RDSR);
		/* Send a dummy byte to generate the clock needed by the SRAM 
		and put the value of the status register in SRAM_Status variable */
		Status = SPI_SRAM_SendByte(Dummy_Byte);
		/* Deselect the SRAM: Chip Select high */
		
		SPI_SRAM_CS_HIGH();
		
		if(TimeOutTimer==0)
		{
				return;
		}
		   
	} while((Status & WIP_Flag) == SET); /* Write in progress */
        
}
/** @} */
